package com.dc.schedule.client.context;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 供全局调用查询当前执行任务线程的上下文
 * 在执行的任务中可以通过 ClientExecutionContextManager.getINSTANCE().getCurrentExecutionContext() 获取调度任务上下文
 *
 * @author Diamon
 */
public class ClientExecutionContextManager {
    
    private ClientExecutionContextManager() {
    }
    
    private static final ClientExecutionContextManager INSTANCE = new ClientExecutionContextManager();
    
    public static ClientExecutionContextManager getINSTANCE() {
        return INSTANCE;
    }
    
    private final ThreadLocal<ClientTaskExecutionContext> clientTaskExecutionContextThreadLocal
            = new ThreadLocal<>();
    /**
     * 使用线程名绑定上下文， 用于，。。log4j2 之类的异步打印日志上下文获取
     * 但是由于Java里面的额线程名称是可以重复的， 所以需要谨慎考虑线程池的构建
     */
    private final Map<String, ClientTaskExecutionContext> threadNameClientTaskExecutionContextMap
            = Collections.synchronizedMap(new LinkedHashMap<>(8));
    /**
     * 维护正在执行的任务, 用于任务查询, 任务终止
     */
    private final Map<Long, ClientTaskExecutionContext> executionIdClientTaskExecutionContextMap
            = Collections.synchronizedMap(new LinkedHashMap<>(8));
    
    public ClientTaskExecutionContext getCurrentExecutionContext() {
        return clientTaskExecutionContextThreadLocal.get();
    }
    
    public ClientTaskExecutionContext getTargetThreadExecutionContext(String threadName) {
        return threadNameClientTaskExecutionContextMap.get(threadName);
    }
    
    /**
     * 要求在当前执行线程内执行
     * <p>任务结束的时候记得释放</p>
     *
     * @param context 要绑定的上下文
     */
    public void bindContext(ClientTaskExecutionContext context) {
        clientTaskExecutionContextThreadLocal.set(context);
        threadNameClientTaskExecutionContextMap.put(Thread.currentThread().getName(), context);
        executionIdClientTaskExecutionContextMap.put(context.getFireTaskContext()
                                                             .getExecutionId(), context);
    }
    
    /**
     * 释放线程绑定的全局上下文
     */
    public void release() {
        final ClientTaskExecutionContext context = clientTaskExecutionContextThreadLocal.get();
        clientTaskExecutionContextThreadLocal.remove();
        threadNameClientTaskExecutionContextMap.remove(Thread.currentThread().getName());
        if (context != null) {
            executionIdClientTaskExecutionContextMap.remove(context.getFireTaskContext().getExecutionId());
        }
    }
}
